#!/usr/bin/env python
######################################################################
##
## Copyright (C) 2006,  Simon Kagstrom
##
## Filename:	  mips2java
## Author:		Simon Kagstrom <ska@bth.se>
## Description:   MIPS binary -> Java bytecode compiler.
##
## $Id: cibyl-mips2java 15768 2007-07-15 13:48:45Z ska $
##
######################################################################
import sys, os, struct, re
from optparse import OptionParser

try:
	import psyco
	psyco.profile()
except:
	# No psyco
	pass

sys.path.append('%s/python/' % sys.path[0])

from Cibyl.BinaryTranslation.translator import Controller
from Cibyl.BinaryTranslation.Mips import instruction
from Cibyl.BinaryTranslation.translator import config
from Cibyl.CompiledHelpers.helpers import *

# Handling of the -I option is done through this callback
syscallDirectories = []
def includeCallback(option, opt, value, parser):
	syscallDirectories.append(value)

defines = []
def defineCallback(option, opt, value, parser):
	defines.append("-D%s" % value)

usage = """usage: %prog [options] infile

Recompile the big-endian MIPS ELF file 'infile' to Java bytecode
assembly. The 'dir' directories are the directories with the C headers
generated by cibyl-generate-c-headers.
"""
parser = OptionParser(usage = usage)
parser.add_option("-d", "--out-directory",
				  help="Write the outfiles to DIRECTORY instad of '.'",
		  dest="outDirectory", metavar="DIRECTORY", default=".")
parser.add_option("-I", "--include", action="callback", help="Directory to search for header files",
		  type="str", nargs=1,
		  callback=includeCallback)
parser.add_option("-g", "--debug", action="store_true", default=False,
		  help="""Turn on generation of debugging information""",
		  dest="debug")
parser.add_option("-O", "--optimize-all", action="store_true", default=False,
				  help="""Enable all optimizations except optimize-inline-all-builtins and
optimize-colocate-functions""",
		  dest="optimize_all")
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
				  help="Be verbose")
parser.add_option("--only-translate", action="store_true", dest="onlyTranslate", default=False,
				  help="""Only run the translation step, do not compile generated source files
into .class files""")
parser.add_option("-D", "--define", action="callback", help="""define a value for the preprocessor
(cpp). Used e.g., for jsr075 (FileConnection), which needs -D JSR075. You can pass multiple -D
arguments""",
		  type="str", nargs=1,
		  callback=defineCallback)
parser.add_option("--trace-function-calls", action="store_true", dest="traceFunctionCalls", default=False,
				  help="Generate a trace of function calls")
parser.add_option("--trace-range", dest="traceRange", default=None,
				  help="Generate an instruction trace for the range RANGE (e.g., 0x100000,0x1000200)",
		  metavar="RANGE")
parser.add_option("--memory-debug", action="store_true", default=False,
				  help="Enable memory debugging (setup in CRunTime.java)",
		  dest="memoryDebug")
parser.add_option("--save-temps", action="store_true", default=False,
				  help="Do not remove temporary files",
		  dest="saveTemps")
parser.add_option("--single-class", action="store_true", default=False,
				  help="Generate only one class instead of splitting in many",
		  dest="singleClass")
parser.add_option("--operand-stack-limit", default=None,
				  help="""Set the limit of the operand stack for generated methods. Default is
to set the limit according to the actual used stack size (usually between 4 and 11)""",
		  dest="operandStackLimit", metavar="NR")
parser.add_option("--class-size-limit", default=None,
				  help="""Set the size limit of generated classes (approximation). After the
limit is passed, a new class will be created""",
		  dest="classSizeLimit", metavar="BYTES")

parser.add_option("--call-table-hierarchy", default=1,
				  help="""Split the call table in NR hiearchies (workaround a JVM bug on some
phones). Default is 1 (no hierarchy)""",
		  dest="callTableHierarchy", metavar="NR")
parser.add_option("--use-direct-memory-references", action="store_true", default=False,
		  help="""Emit the memory vector directly for each memory access instead of
using an aload reference (which saves space and might improve
performance). This is a workaround against problems on some phones.""",
		  dest="aloadMemory")
parser.add_option("--no-function-pruning", action="store_false", default=True,
				  help="Don't prune unused functions (some GCC versions will not allow function pruning)",
		  dest="pruneUnusedFunctions")
parser.add_option("--peephole-iterations", default=2,
				  help="""Set the number of iterations to run the peephole optimizer (default 2)""",
		  dest="peepholeIterations", metavar="NR")
parser.add_option("--javac-command-line", default=None,
				  help="""Set the javac command line. Default 'javac -source 1.4 -bootclasspath
$WTK_PATH/lib/cldcapi11.jar:$WTK_PATH/lib/midpapi20.jar'. javac must
support the -d and -classpath options, which are passed by the tool""",
		  dest="javacCommandLine", metavar="COMMAND_LINE")
parser.add_option("--jasmin-command-line", default=None,
				  help="""Set the jasmin command line. Default 'jasmin'. jasmin must support the
-d which is passed by the tool""",
		  dest="jasminCommandLine", metavar="COMMAND_LINE")
parser.add_option("--optimize-use-profile", default=None,
				  help="""Use profile-based optimization from a J2ME profiler file""",
		  dest="profileFile", metavar="PROFILE.prf")
parser.add_option("--optimize-peephole", action="store_true", default=False,
		  help="""Turn on the peephole optimizer""",
		  dest="peepholeOptimize")
parser.add_option("--optimize-value-tracking", action="store_true", default=False,
		  help="""Turn on the register value tracker (constant propagation). This
improves for example memory accesses to constant addresses""",
		  dest="registerValueTracking")
parser.add_option("--optimize-multiplications", action="store_true", default=False,
				  help="""Turn on the mult/div optimizations. This improves the performance of
most multiplications and divisions""",
		  dest="multiplication_optimization")
parser.add_option("--optimize-indirect-calls", action="store_true", default=False,
				  help="""Turn on optimization of indirect call generation. This will reduce the
size of the generated class file""",
		  dest="doOptimizeIndirectCalls")
parser.add_option("--optimize-inline-all-builtins", action="store_true", default=False,
				  help="""Turn on inlining of ALL builtin functions (e.g., floating point helper
functions). This will increase the size of the executable, but may improve performance""",
		  dest="doInlineAllBuiltins")
parser.add_option("--optimize-inline-builtins", dest="inlineBuiltinFunctions", default=None,
				  help="Inline builtins for FUNCTIONS (comma-separated)",
		  metavar="FUNCTIONS")
parser.add_option("--optimize-colocate-functions", dest="colocateFunctions", default=None,
				  help="""Colocate FUNCTIONS into one Java method (comma-separated). Pass this
option multiple times to colocate functions into different methods""",
		  metavar="FUNCTIONS")
parser.add_option("--optimize-register-scheduling", action="store_true", default=False,
				  help="""Perform register scheduling to reduce the number of registers used by
a function. The number of arguments passed to leaf functions can also
be reduced. This reduces the size of the executable and can provide
performance advantages for frequently called short functions""",
		  dest="optimize_register_scheduling")

(options, args) = parser.parse_args()
if len(args) != 1:
	parser.error("incorrect number of arguments.\n\nInvoke with --help for arguments")
infile = args[0]

config.aloadMemory = not options.aloadMemory # OK, this looks strange. I agree
config.doMultOptimization = options.multiplication_optimization
config.doRegisterScheduling = options.optimize_register_scheduling
config.doInlineAllBuiltins = options.doInlineAllBuiltins
config.doOptimizeIndirectCalls = options.doOptimizeIndirectCalls
config.doPeepholeOptimize = options.peepholeOptimize
config.doRegisterValueTracking = options.registerValueTracking

config.pruneUnusedFunctions = options.pruneUnusedFunctions

if options.onlyTranslate:
	config.onlyTranslate = True
	options.saveTemps = True

config.saveTemps = options.saveTemps
config.traceFunctionCalls = options.traceFunctionCalls
config.verbose = options.verbose
config.outDirectory = options.outDirectory
config.dataOutFilename = "program.data.bin"

config.memoryDebug = options.memoryDebug

if options.jasminCommandLine:
	config.jasmin = options.jasminCommandLine
if options.javacCommandLine:
	config.javac = options.javacCommandLine

config.callTableHierarchy = int(options.callTableHierarchy)
config.peepholeIterations = int(options.peepholeIterations)

config.infile = infile
config.profileFile = options.profileFile

config.defines = defines

if options.debug:
	config.debug = options.debug
	options.singleClass = True # Force a single class

# Setup the class size limit
if options.singleClass:
	config.classSizeLimit = (1<<30) # Practically infinite

if options.classSizeLimit:
	config.classSizeLimit = int(options.classSizeLimit)

if options.traceRange:
	a,b = options.traceRange.split(",")
	config.traceStart = int(a, 16)
	config.traceEnd = int(b, 16)

if options.operandStackLimit:
	config.operandStackLimit = int(options.operandStackLimit)

if options.inlineBuiltinFunctions:
	config.inlineBuiltinFunctions = options.inlineBuiltinFunctions.split(",")

if options.colocateFunctions:
	config.colocateFunctions = options.colocateFunctions.split(",")
	# Depends on register scheduling to pass the function address,
	# conflicts with debug
	config.doRegisterScheduling = True
	config.debug = False

if options.optimize_all:
	config.doMultOptimization = True
	config.doRegisterScheduling = True
	config.doPeepholeOptimize = True
	config.doRegisterValueTracking = True

if config.memoryDebug:
	instruction.setupMemoryDebug()

usePythonCompiler = False

# The C++ compiler cannot be used with some options
if config.doRegisterValueTracking or config.colocateFunctions or options.inlineBuiltinFunctions or options.debug:
    usePythonCompiler = True

def doCompile():
	global controller
	config.checkEnvironment()
	controller = Controller(config.infile, syscallDirectories)
	controller.compile()

def doCppCompile():
    doTranslation(config.infile, syscallDirectories)
    doJasmin(config.outDirectory + "/Cibyl.j")
    doJavac(config.outDirectory + "/CibylCallTable.java")

if usePythonCompiler:
    doCompile()
else:
    doCppCompile()
